(function(){
	var htmlEncodeDict = { '"': "quot", "<": "lt", ">": "gt", "&": "amp", " ": "nbsp" };

	/**
	 * 格式化函数 $原样输出
	 * @param {String} template 模板
	 * @param {Object} json 数据项
	 */
	function format(template, json) {
		return template.replace(/(\$)\{(.*?)\}/g, function(all, flag, key) {
			return json && (key in json) ? json[key] : "";
		});
	}

	function currentStyle(element) {
		return element.currentStyle || document.defaultView.getComputedStyle(element, null);
	}

	/**
	 * HTML编码
	 * @param {Object} html 
	 */
	function encodeHTML(html) {
		return String(html).replace(/["<>& ]/g, function(all) {
			return "&" + htmlEncodeDict[all] + ";";
		});
	}

	/**
	 * 将嵌套数组输出为字符串
	 * @param {Array} output 数组，主要是处理异步请求
	 */
	function outputText(output) {
		var result = [];
		for (var i = 0; i < output.length; i++) {
			var item = output[i];
			if (item instanceof Array)
					result.push(outputText(item));
			else result.push(item);
		}
		return result.join("");
	}

	var line = [];
	function scanElement(output, element, layer) {
		if (!element || !/^(1|3)$/.test(element.nodeType)) return;
		switch(element.nodeType) {
			case 3:
				output.push(encodeHTML(element.nodeValue));
				return;
		}
		if (!element.tagName) return;
		var style = currentStyle(element);
		if (style.display == "none") return; // 不可见
		
		var tagName = String(element.tagName).toLowerCase();
		var space = new Array(layer + 1).join("\t");
		var include = [];
		var attribute = [];
		switch (tagName) {
			case "":
			case "script":
				return;
			case "img":
			case "input":
			case "br":
			case "hr":
			case "col":
			case "area":
				output.push("<", tagName, attribute, "/>");
				break;
			default:
				output.push("<", tagName, attribute, ">", include, "</", tagName,  ">");
				break;
		}
		switch(tagName) {
			case "img":
				attribute.push(' src="', element.getAttribute("src") , '"');
				break;
			case "input":
				attribute.push(' type="', element.getAttribute("type") , '"');
				attribute.push(' value="', element.getAttribute("value") , '"');
				break;
			case "textarea":
				include.push(encodeHTML(element.value));
				break;
		}
		if (style.visibility == "hidden") return;
		for (var i = 0, l = element.childNodes.length; i < l; i++) {
			var child = element.childNodes[i];
			scanElement(include, child, layer + 1);
		}
	}
	scanElement(line, document.body, 0);
	alert(outputText(line));
})();